package com.springboot.lock.lock_zk;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.UUID;

/**
 * @Author: TongRui乀
 * @Date: 2020-08-07 10:01
 * @description：
 */
@Slf4j
@Aspect
@Component
public class ZkLockAspect {

    @Autowired
    private com.springboot.lock.lock_zk.lock.ZkLock zkLock;

    @Pointcut("@annotation(com.springboot.lock.lock_zk.ZkLock)")
    public void pointCut() {
    }


    @Around("pointCut()")
    public Object lockAspect(ProceedingJoinPoint pj) {

        // 计算 key
        String key = calculateKey(pj);
        // 计算 value
        String value = UUID.randomUUID().toString();
        // 获取锁的释放时间
        ZkLock zkLock = getAnnotation(pj, ZkLock.class);
        // 获取锁
        String lock = this.zkLock.tryLock("/"+key, value, zkLock.expire(), zkLock.waitTime());

        if (StringUtils.isEmpty(lock)) {
            return "获取锁失败";
        }
        log.info("获取锁成功:{}", Thread.currentThread().getName());
        // 调用目标方法
        Object proceed = null;
        try {
            proceed = pj.proceed();
        } catch (Throwable throwable) {

            throwable.printStackTrace();
        } finally {
            // 释放锁
            this.zkLock.unLock(lock);
        }
        return proceed;
    }

    /**
     * 解析出注解
     *
     * @param pj
     * @param clazz
     * @param <T>
     * @return
     */
    private <T extends Annotation> T getAnnotation(ProceedingJoinPoint pj, Class<T> clazz) {

        MethodSignature signature = (MethodSignature) pj.getSignature();

        Method method = signature.getMethod();

        return method.getAnnotation(clazz);
    }

    private String calculateKey(ProceedingJoinPoint pj) {

        MethodSignature signature = (MethodSignature) pj.getSignature();

        Object[] args = pj.getArgs();

        Method method = signature.getMethod();

        Parameter[] parameters = method.getParameters();

        // 获取到锁注解
        ZkLock zkLock = method.getAnnotation(ZkLock.class);

        if (zkLock == null) {
            throw new RuntimeException("不使用分布式锁");
        }

        String[] columns = zkLock.columns();

        StringBuilder sb = new StringBuilder(method.getName());

        sb.append(":");

        for (int i = 0; i < parameters.length; i++) {
            if (columns[i].equals(parameters[i].getName())) {
                sb.append(args[i]).append(",");
            }
        }

        return sb.toString();
    }

}
